home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / programming / source / graphicgems4.lha / GemsIV / curve_isect / Bezier.cc next >
Encoding:
C/C++ Source or Header  |  1995-02-06  |  12.4 KB  |  368 lines

  1. #include <stddef.h> // for size_t
  2. // #include <sys/stdtypes.h> // for size_t, on some systems
  3. #include "Bezier.h"
  4. #include <math.h>
  5. /* The value of 1.0 / (1L<<23) is float machine epsilon. */
  6. #ifdef FLOAT_ACCURACY
  7. #define INV_EPS (1L<<23)
  8. #else
  9. /* The value of 1.0 / (1L<<14) is enough for most applications */
  10. #define INV_EPS (1L<<14)
  11. #endif
  12. #define log2(x) (log(x)/log(2.))
  13.  
  14. extern "C" void qsort( char *base, int nel, size_t width, int (*compar)(void *, void *));
  15.  
  16. int compare_doubles( void *a, void *b )
  17.     {
  18.     register double *A = (double *)a, *B = (double *)b;
  19.     return( *A > *B )?1:(*A < *B ? -1 : 0 );
  20.     }
  21.  
  22. void Sort( double *array, int length )
  23.     {
  24.     qsort( (char *)array, length, sizeof( double ), compare_doubles );
  25.     }
  26.     
  27. /*
  28.  * Split the curve at the midpoint, returning an array with the two parts
  29.  * Temporary storage is minimized by using part of the storage for the result
  30.  * to hold an intermediate value until it is no longer needed.
  31.  */
  32. #define left r[0]
  33. #define right r[1]
  34. Bezier *Bezier::Split()
  35.     {
  36.     Bezier *r = new Bezier[2];
  37.     (left.p0 = p0)->refcount++;
  38.     (right.p3 = p3)->refcount++;
  39.     left.p1 = new point(  p0, p1, 1);
  40.     right.p2 = new point( p2, p3, 1);
  41.     right.p1 = new point( p1, p2, 1); // temporary holding spot
  42.     left.p2 = new point ( left.p1, right.p1, 1);
  43.     *right.p1 = mid( right.p1, right.p2 ); // Real value this time
  44.     left.p3 = right.p0 = new point( left.p2, right.p1, 2 );
  45.     return r;
  46.     }
  47. #undef left
  48. #undef right
  49.  
  50.     
  51. /*
  52. * Test the bounding boxes of two Bezier curves for interference.
  53. * Several observations:
  54. *    First, it is cheaper to compute the bounding box of the second curve
  55. *    and test its bounding box for interference than to use a more direct
  56. *    approach of comparing all control points of the second curve with 
  57. *    the various edges of the bounding box of the first curve to test
  58. *     for interference.
  59. *    Second, after a few subdivisions it is highly probable that two corners
  60. *    of the bounding box of a given Bezier curve are the first and last 
  61. *    control point.  Once this happens once, it happens for all subsequent
  62. *    subcurves.  It might be worth putting in a test and then short-circuit
  63. *    code for further subdivision levels.
  64. *    Third, in the final comparison (the interference test) the comparisons
  65. *    should both permit equality.  We want to find intersections even if they
  66. *    occur at the ends of segments.
  67. *    Finally, there are tighter bounding boxes that can be derived. It isn't
  68. *    clear whether the higher probability of rejection (and hence fewer
  69. *    subdivisions and tests) is worth the extra work.
  70. */
  71. int IntersectBB( Bezier a, Bezier b )
  72.     {
  73.     // Compute bounding box for a
  74.     double minax, maxax, minay, maxay;
  75.     if( a.p0->x > a.p3->x )     // These are the most likely to be extremal
  76.     minax = a.p3->x, maxax = a.p0->x;
  77.     else
  78.     minax = a.p0->x, maxax = a.p3->x;
  79.     if( a.p2->x < minax )
  80.     minax = a.p2->x;
  81.     else if( a.p2->x > maxax )
  82.     maxax = a.p2->x;
  83.     if( a.p1->x < minax )
  84.     minax = a.p1->x;
  85.     else if( a.p1->x > maxax )
  86.     maxax = a.p1->x;
  87.     if( a.p0->y > a.p3->y )         
  88.     minay = a.p3->y, maxay = a.p0->y;
  89.     else
  90.     minay = a.p0->y, maxay = a.p3->y;
  91.     if( a.p2->y < minay )
  92.     minay = a.p2->y;
  93.     else if( a.p2->y > maxay )
  94.     maxay = a.p2->y;
  95.     if( a.p1->y < minay )
  96.     minay = a.p1->y;
  97.     else if( a.p1->y > maxay )
  98.     maxay = a.p1->y;
  99.     // Compute bounding box for b
  100.     double minbx, maxbx, minby, maxby;
  101.     if( b.p0->x > b.p3->x )         
  102.     minbx = b.p3->x, maxbx = b.p0->x;
  103.     else
  104.     minbx = b.p0->x, maxbx = b.p3->x;
  105.     if( b.p2->x < minbx )
  106.     minbx = b.p2->x;
  107.     else if( b.p2->x > maxbx )
  108.     maxbx = b.p2->x;
  109.     if( b.p1->x < minbx )
  110.     minbx = b.p1->x;
  111.     else if( b.p1->x > maxbx )
  112.     maxbx = b.p1->x;
  113.     if( b.p0->y > b.p3->y )         
  114.     minby = b.p3->y, maxby = b.p0->y;
  115.     else
  116.     minby = b.p0->y, maxby = b.p3->y;
  117.     if( b.p2->y < minby )
  118.     minby = b.p2->y;
  119.     else if( b.p2->y > maxby )
  120.     maxby = b.p2->y;
  121.     if( b.p1->y < minby )
  122.     minby = b.p1->y;
  123.     else if( b.p1->y > maxby )
  124.     maxby = b.p1->y;
  125.     // Test bounding box of b against bounding box of a
  126.     if( ( minax > maxbx ) || ( minay > maxby )  // Not >= : need boundary case
  127.     || ( minbx > maxax ) || ( minby > maxay ) )
  128.     return 0; // they don't intersect
  129.     else
  130.     return 1; // they intersect
  131.     }
  132.     
  133. /* 
  134. * Recursively intersect two curves keeping track of their real parameters 
  135. * and depths of intersection.
  136. * The results are returned in a 2-D array of doubles indicating the parameters
  137. * for which intersections are found.  The parameters are in the order the
  138. * intersections were found, which is probably not in sorted order.
  139. * When an intersection is found, the parameter value for each of the two 
  140. * is stored in the index elements array, and the index is incremented.
  141. * If either of the curves has subdivisions left before it is straight
  142. *    (depth > 0)
  143. * that curve (possibly both) is (are) subdivided at its (their) midpoint(s).
  144. * the depth(s) is (are) decremented, and the parameter value(s) corresponding
  145. * to the midpoints(s) is (are) computed.
  146. * Then each of the subcurves of one curve is intersected with each of the 
  147. * subcurves of the other curve, first by testing the bounding boxes for
  148. * interference.  If there is any bounding box interference, the corresponding
  149. * subcurves are recursively intersected.
  150. * If neither curve has subdivisions left, the line segments from the first
  151. * to last control point of each segment are intersected.  (Actually the 
  152. * only the parameter value corresponding to the intersection point is found).
  153. *
  154. * The apriori flatness test is probably more efficient than testing at each
  155. * level of recursion, although a test after three or four levels would
  156. * probably be worthwhile, since many curves become flat faster than their 
  157. * asymptotic rate for the first few levels of recursion.
  158. *
  159. * The bounding box test fails much more frequently than it succeeds, providing
  160. * substantial pruning of the search space.
  161. *
  162. * Each (sub)curve is subdivided only once, hence it is not possible that for 
  163. * one final line intersection test the subdivision was at one level, while
  164. * for another final line intersection test the subdivision (of the same curve)
  165. * was at another.  Since the line segments share endpoints, the intersection
  166. * is robust: a near-tangential intersection will yield zero or two
  167. * intersections.
  168. */
  169. void RecursivelyIntersect( Bezier a, double t0, double t1, int deptha,
  170.                Bezier b, double u0, double u1, int depthb,
  171.                double **parameters, int &index )
  172.     {
  173.     if( deptha > 0 )
  174.     {
  175.     Bezier *A = a.Split();
  176.     double tmid = (t0+t1)*0.5;
  177.     deptha--;
  178.     if( depthb > 0 )
  179.         {
  180.         Bezier *B = b.Split();
  181.         double umid = (u0+u1)*0.5;
  182.         depthb--;
  183.         if( IntersectBB( A[0], B[0] ) )
  184.         RecursivelyIntersect( A[0], t0, tmid, deptha,
  185.                       B[0], u0, umid, depthb,
  186.                       parameters, index );
  187.         if( IntersectBB( A[1], B[0] ) )
  188.         RecursivelyIntersect( A[1], tmid, t1, deptha,
  189.                       B[0], u0, umid, depthb,
  190.                       parameters, index );
  191.         if( IntersectBB( A[0], B[1] ) )
  192.         RecursivelyIntersect( A[0], t0, tmid, deptha,
  193.                       B[1], umid, u1, depthb,
  194.                       parameters, index );
  195.         if( IntersectBB( A[1], B[1] ) )
  196.         RecursivelyIntersect( A[1], tmid, t1, deptha,
  197.                       B[1], umid, u1, depthb,
  198.                       parameters, index );
  199.         }
  200.     else
  201.         {
  202.         if( IntersectBB( A[0], b ) )
  203.         RecursivelyIntersect( A[0], t0, tmid, deptha,
  204.                       b, u0, u1, depthb,
  205.                       parameters, index );
  206.         if( IntersectBB( A[1], b ) )
  207.         RecursivelyIntersect( A[1], tmid, t1, deptha,
  208.                       b, u0, u1, depthb,
  209.                       parameters, index );
  210.         }
  211.     }
  212.     else
  213.     if( depthb > 0 )
  214.         {
  215.         Bezier *B = b.Split();
  216.         double umid = (u0 + u1)*0.5;
  217.         depthb--;
  218.         if( IntersectBB( a, B[0] ) )
  219.         RecursivelyIntersect( a, t0, t1, deptha,
  220.                       B[0], u0, umid, depthb,
  221.                       parameters, index );
  222.         if( IntersectBB( a, B[1] ) )
  223.         RecursivelyIntersect( a, t0, t1, deptha,
  224.                       B[0], umid, u1, depthb,
  225.                       parameters, index );
  226.         }
  227.     else // Both segments are fully subdivided; now do line segments
  228.         {
  229.         double xlk = a.p3->x - a.p0->x;
  230.         double ylk = a.p3->y - a.p0->y;
  231.         double xnm = b.p3->x - b.p0->x;
  232.         double ynm = b.p3->y - b.p0->y;
  233.         double xmk = b.p0->x - a.p0->x;
  234.         double ymk = b.p0->y - a.p0->y;
  235.         double det = xnm * ylk - ynm * xlk;
  236.         if( 1.0 + det == 1.0 )
  237.         return;
  238.         else
  239.         {
  240.         double detinv = 1.0 / det;
  241.         double s = ( xnm * ymk - ynm *xmk ) * detinv;
  242.         double t = ( xlk * ymk - ylk * xmk ) * detinv;
  243.         if( ( s < 0.0 ) || ( s > 1.0 ) || ( t < 0.0 ) || ( t > 1.0 ) )
  244.             return;
  245.         parameters[0][index] = t0 + s * ( t1 - t0 );
  246.         parameters[1][index] = u0 + t * ( u1 - u0 );
  247.         index++;
  248.         }
  249.         }
  250.     }
  251.  
  252. inline double log4( double x ) { return 0.5 * log2( x ); }
  253.     
  254. /*
  255.  * Wang's theorem is used to estimate the level of subdivision required,
  256.  * but only if the bounding boxes interfere at the top level.
  257.  * Assuming there is a possible intersection, RecursivelyIntersect is
  258.  * used to find all the parameters corresponding to intersection points.
  259.  * these are then sorted and returned in an array.
  260.  */
  261.  
  262. double ** FindIntersections( Bezier a, Bezier b )
  263.     {
  264.     double **parameters = new double *[2];
  265.     parameters[0] = new double[9];
  266.     parameters[1] = new double[9];
  267.     int index = 0;
  268.     if( IntersectBB( a, b ) )
  269.     {
  270.     vector la1 = vabs( ( *(a.p2) - *(a.p1) ) - ( *(a.p1) - *(a.p0) ) );
  271.     vector la2 = vabs( ( *(a.p3) - *(a.p2) ) - ( *(a.p2) - *(a.p1) ) );
  272.     vector la;
  273.     if( la1.x > la2.x ) la.x = la1.x; else la.x = la2.x;
  274.     if( la1.y > la2.y ) la.y = la1.y; else la.y = la2.y;
  275.     vector lb1 = vabs( ( *(b.p2) - *(b.p1) ) - ( *(b.p1) - *(b.p0) ) );
  276.     vector lb2 = vabs( ( *(b.p3) - *(b.p2) ) - ( *(b.p2) - *(b.p1) ) );
  277.     vector lb;
  278.     if( lb1.x > lb2.x ) lb.x = lb1.x; else lb.x = lb2.x;
  279.     if( lb1.y > lb2.y ) lb.y = lb1.y; else lb.y = lb2.y;
  280.     double l0;
  281.     if( la.x > la.y ) 
  282.         l0 = la.x;
  283.     else 
  284.         l0 = la.y;
  285.     int ra;
  286.     if( l0 * 0.75 * M_SQRT2 + 1.0 == 1.0 ) 
  287.         ra = 0;
  288.     else
  289.         ra = (int)ceil( log4( M_SQRT2 * 6.0 / 8.0 * INV_EPS * l0 ) );
  290.     if( lb.x > lb.y ) 
  291.         l0 = lb.x;
  292.     else 
  293.         l0 = lb.y;
  294.     int rb;
  295.     if( l0 * 0.75 * M_SQRT2 + 1.0 == 1.0 ) 
  296.         rb = 0;
  297.     else
  298.         rb = (int)ceil(log4( M_SQRT2 * 6.0 / 8.0 * INV_EPS * l0 ) );
  299.     RecursivelyIntersect( a, 0., 1., ra, b, 0., 1., rb, parameters, index );
  300.     }
  301.     if( index < 9 )
  302.     parameters[0][index] = parameters[1][index] = -1.0;
  303.     Sort( parameters[0], index );
  304.     Sort( parameters[1], index );
  305.     return parameters;
  306.     }
  307.  
  308. void
  309. Bezier::ParameterSplitLeft( double t, Bezier &left )
  310.     {
  311.     left.p0 = p0;
  312.     left.p1 = new point( *p0 + t * ( *p1 - *p0 ) );
  313.     left.p2 = new point( *p1 + t * ( *p2 - *p1 ) ); // temporary holding spot
  314.     p2->refcount--;
  315.     p2 = new point( *p2 + t * ( *p3 - *p2 ) );
  316.     p1->refcount--;
  317.     p1 = new point( *(left.p2) + t * ( *p2 - *(left.p2) ) );
  318.     *(left.p2) = ( *(left.p1) + t * ( *(left.p2) - *(left.p1) ) );
  319.     (left.p3 = p0 = new point(*(left.p2) + t * (*(p1)-*(left.p2))))->refcount++;
  320.     left.p0->refcount++; left.p1->refcount++; 
  321.     left.p2->refcount++; left.p3->refcount++;
  322.     }
  323.     
  324. /*
  325.  * Intersect two curves, returning an array of two arrays of curves.
  326.  * The first array of curves corresponds to `this' curve, the second 
  327.  * corresponds to curve B, passed in.
  328.  * The intersection parameter values are computed by FindIntersections,
  329.  * and they come back in the range 0..1, using the original parameterization.
  330.  * Once one segment has been removed, ie the curve is split at splitT, the
  331.  * parameterization of the second half is from 0..1, so the parameter for
  332.  * the next split point, if any, must be adjusted.
  333.  * If we split at t[i], the split point at t[i+1] is 
  334.  * ( t[i+1] - t[i] ) / ( t - t[i] ) of the way to the end from the new
  335.  * start point.
  336.  */
  337.  
  338. Bezier **Bezier::Intersect( Bezier B )
  339.     {
  340.     // The return from FindIntersections will decrement all refcounts.
  341.     // (a c++-ism)
  342.     B.p0->refcount++; B.p1->refcount++; B.p2->refcount++; B.p3->refcount++; 
  343.     Bezier **rvalue = new Bezier *[2];
  344.     rvalue[0] = new Bezier[10];
  345.     rvalue[1] = new Bezier[10];
  346.     double **t = FindIntersections( *this, B );
  347.     int index = 0;
  348.     if( t[0][0] > -0.5 )
  349.     {
  350.     ParameterSplitLeft( t[0][0], rvalue[0][0] );
  351.     B.ParameterSplitLeft( t[1][0], rvalue[1][0] );
  352.     index++;
  353.     while( t[0][index] > -0.5 && index < 9 )
  354.         {
  355.         double splitT = (t[0][index] - t[0][index-1])/(1.0 - t[0][index-1]);
  356.         ParameterSplitLeft( splitT, rvalue[0][index] );
  357.         splitT = (t[1][index] - t[1][index-1])/(1.0 - t[1][index-1]);
  358.         B.ParameterSplitLeft( splitT, rvalue[1][index] );
  359.         index++;
  360.         }
  361.     }
  362.     rvalue[0][index] = *this;
  363.     rvalue[1][index] = B;
  364.     return rvalue;
  365.     }
  366.